package com.thz.system.utils;

import com.thz.common.enums.ErrorCode;
import com.thz.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * FFmpeg工具类 - 用于视频转GIF
 * @author THZ
 * @date 2025
 */
@Slf4j
@Component
public class FFmpegUtils {

    /**
     * FFmpeg命令路径（可通过配置文件覆盖）
     */
    @Value("${ffmpeg.path:ffmpeg}")
    private static String FFMPEG_PATH;

    /**
     * FFprobe命令路径（可通过配置文件覆盖）
     */
    @Value("${ffprobe.path:ffprobe}")
    private static String FFPROBE_PATH;

    /**
     * 设置 FFmpeg 路径（供配置注入使用）
     */
    @Value("${ffmpeg.path:ffmpeg}")
    public void setFfmpegPath(String ffmpegPath) {
        FFMPEG_PATH = ffmpegPath;
        log.info("FFmpeg路径已设置为: {}", FFMPEG_PATH);
    }

    /**
     * 设置 FFprobe 路径（供配置注入使用）
     */
    @Value("${ffprobe.path:ffprobe}")
    public void setFfprobePath(String ffprobePath) {
        FFPROBE_PATH = ffprobePath;
        log.info("FFprobe路径已设置为: {}", FFPROBE_PATH);
    }


    /**
     * 转换视频为GIF（已优化性能）
     *
     * @param inputPath 输入视频路径
     * @param outputPath 输出GIF路径
     * @param width GIF宽度（null则保持原始比例）
     * @param height GIF高度（null则保持原始比例）
     * @param fps 帧率（1-30，控制GIF的流畅度，默认10）
     *            - fps越高，GIF越流畅（更多帧），但文件越大
     *            - fps越低，GIF越卡顿（更少帧），但文件越小
     *            - 播放速度与原视频保持一致（使用fps滤镜提取帧 + -r参数控制输出帧率）
     *            - 例如：fps=30 表示从视频每秒提取30帧，-r 30 表示GIF以30帧/秒播放
     * @param startTime 开始时间（秒，null则从头开始）
     * @param duration 持续时间（秒，null则转换全部）
     * @param quality 质量（1-100，默认80）
     * @param cropX 裁剪起始X坐标（null则不裁剪）
     * @param cropY 裁剪起始Y坐标（null则不裁剪）
     * @param cropWidth 裁剪宽度（null则不裁剪）
     * @param cropHeight 裁剪高度（null则不裁剪）
     */
    public static void convertVideoToGif(String inputPath, String outputPath,
                                         Integer width, Integer height, Integer fps,
                                         Double startTime, Double duration, Integer quality,
                                         Integer cropX, Integer cropY, Integer cropWidth, Integer cropHeight) {
        try {
            List<String> command = new ArrayList<>();
            command.add(FFMPEG_PATH);

            // 性能优化：先定位到开始时间点（放在-i之前更快）
            if (startTime != null && startTime > 0) {
                command.add("-ss");
                command.add(String.valueOf(startTime));
            }

            // 计算视频时长（用于智能优化策略）
            double videoDuration = (duration != null && duration > 0) ? duration : 10.0;

            // 智能硬件加速：根据视频时长决定是否使用
            // 【平衡优化】智能硬件加速：降低触发阈值
            // 硬件加速有初始化开销，但对5秒以上视频已有明显收益
            if (videoDuration >= 5) {
                // 性能优化：使用硬件加速（如果可用）
                // 尝试使用硬件解码加速，失败则自动回退到软件解码
                command.add("-hwaccel");
                command.add("auto");
                log.info("视频时长: {}秒, 启用硬件加速", videoDuration);
            } else {
                log.info("视频时长: {}秒, 跳过硬件加速（避免初始化开销）", videoDuration);
            }

            // 性能优化：禁用音频处理（GIF不需要音频，提速5-10%）
            command.add("-an");

            command.add("-i");
            command.add(inputPath);

            // 添加持续时间（放在-i之后更精确）
            if (duration != null && duration > 0) {
                command.add("-t");
                command.add(String.valueOf(duration));
            }

            // 构建滤镜参数
            int targetFps = (fps != null && fps > 0) ? fps : 10;
            StringBuilder filter = new StringBuilder();

            // 如果有裁剪参数，先进行裁剪
            if (cropX != null && cropY != null && cropWidth != null && cropHeight != null) {
                filter.append("crop=").append(cropWidth).append(":").append(cropHeight)
                        .append(":").append(cropX).append(":").append(cropY).append(",");
            }

            // 设置帧率（从视频提取帧的频率）
            filter.append("fps=").append(targetFps);

            // 智能分辨率压缩：在保证清晰度的前提下优化转换速度
            // GIF主要用于网页展示，超过1920px宽度意义不大，且会大幅增加转换时间
            Integer finalWidth = width;
            Integer finalHeight = height;

            // 注意：如果宽度或高度为null，表示需要FFmpeg自动计算等比例值，不应该使用默认值
            if (finalWidth != null || finalHeight != null) {
                // 计算实际输出尺寸（考虑裁剪）
                int actualWidth = finalWidth != null ? finalWidth :
                        (cropWidth != null ? cropWidth : -1); // 使用-1表示需要自动计算
                int actualHeight = finalHeight != null ? finalHeight :
                        (cropHeight != null ? cropHeight : -1); // 使用-1表示需要自动计算

                // 智能压缩策略：根据分辨率动态调整
                // 超高分辨率（>1920px）：压缩到1920px（保证清晰度，大幅提速）
                // 高分辨率（1440-1920px）：保持原尺寸或轻微压缩
                // 标准分辨率（<1440px）：保持原尺寸
                int maxRecommendedWidth = 1920; // GIF推荐最大宽度
                int maxRecommendedHeight = 1080; // GIF推荐最大高度

                // 如果为-1，表示需要FFmpeg自动计算，不应该进行压缩处理
                if (actualWidth > 0 && actualHeight > 0 && 
                    (actualWidth > maxRecommendedWidth || actualHeight > maxRecommendedHeight)) {
                    double widthScale = actualWidth > maxRecommendedWidth ?
                            (double) maxRecommendedWidth / actualWidth : 1.0;
                    double heightScale = actualHeight > maxRecommendedHeight ?
                            (double) maxRecommendedHeight / actualHeight : 1.0;
                    double scale = Math.min(widthScale, heightScale); // 使用较小的缩放比例保持宽高比

                    int compressedWidth = (int) Math.round(actualWidth * scale);
                    int compressedHeight = (int) Math.round(actualHeight * scale);

                    // 确保压缩后的尺寸是偶数（GIF编码要求）
                    compressedWidth = compressedWidth % 2 == 0 ? compressedWidth : compressedWidth - 1;
                    compressedHeight = compressedHeight % 2 == 0 ? compressedHeight : compressedHeight - 1;

                    // 更新最终尺寸
                    if (finalWidth != null) {
                        finalWidth = compressedWidth;
                    }
                    if (finalHeight != null) {
                        finalHeight = compressedHeight;
                    }

                    log.info("智能压缩 - 原尺寸: {}x{}, 压缩后: {}x{}, 压缩比例: {:.1f}%, 预计提速: {:.0f}%",
                            actualWidth, actualHeight, compressedWidth, compressedHeight,
                            String.format("%.1f", scale * 100), String.format("%.0f", (1 - scale * scale) * 100));
                }
            }

            // 设置尺寸（使用最快的缩放算法）
            // 对于GIF来说，bilinear算法足够好且速度快2-3倍
            String scaleAlg = "bilinear"; // 比bicubic快2-3倍，质量对GIF来说足够

            if (finalWidth != null && finalHeight != null) {
                filter.append(",scale=").append(finalWidth).append(":").append(finalHeight)
                        .append(":flags=").append(scaleAlg);
            } else if (finalWidth != null) {
                filter.append(",scale=").append(finalWidth).append(":-1:flags=").append(scaleAlg);
            } else if (finalHeight != null) {
                filter.append(",scale=-1:").append(finalHeight).append(":flags=").append(scaleAlg);
            } else {
                // 如果没有指定尺寸，智能限制最大分辨率
                if (cropX == null && cropY == null) {
                    // 默认限制最大宽度为1920px（保证清晰度，同时优化速度）
                    filter.append(",scale='min(1920,iw)':-1:flags=").append(scaleAlg);
                }
            }

            // 高性能调色板生成策略（两阶段优化：先缩小生成调色板，再应用到原尺寸）
            // 这是处理高分辨率GIF的关键优化，可以提速5-10倍
            int maxColors;
            String statsMode;
            int bayerScale;

            // 计算输出分辨率（使用压缩后的尺寸，用于判断是否需要两阶段优化）
            int outputWidth = finalWidth != null ? finalWidth : (width != null ? width : 800);
            int outputHeight = finalHeight != null ? finalHeight : (height != null ? height : 600);
            if (cropWidth != null && cropHeight != null) {
                // 如果使用了裁剪，需要考虑压缩后的裁剪尺寸
                // 这里使用原始裁剪尺寸，因为压缩是在裁剪之后进行的
                outputWidth = cropWidth;
                outputHeight = cropHeight;
                // 如果裁剪尺寸超过推荐值，应用压缩
                if (outputWidth > 1920 || outputHeight > 1080) {
                    double widthScale = outputWidth > 1920 ? 1920.0 / outputWidth : 1.0;
                    double heightScale = outputHeight > 1080 ? 1080.0 / outputHeight : 1.0;
                    double scale = Math.min(widthScale, heightScale);
                    outputWidth = (int) Math.round(outputWidth * scale);
                    outputHeight = (int) Math.round(outputHeight * scale);
                    outputWidth = outputWidth % 2 == 0 ? outputWidth : outputWidth - 1;
                    outputHeight = outputHeight % 2 == 0 ? outputHeight : outputHeight - 1;
                }
            }
            int totalPixels = outputWidth * outputHeight;
            boolean isHighResolution = totalPixels > 1000000;

            // 根据视频时长、质量和分辨率动态调整策略
            // 【色彩优化】提高颜色数和抖动，保证色彩保真度
            int baseMaxColors;
            if (videoDuration < 10) {
                // 短视频：平衡速度和质量
                if (quality != null && quality >= 80) {
                    baseMaxColors = 256;
                    bayerScale = 2;
                } else if (quality != null && quality >= 60) {
                    baseMaxColors = 128;
                    bayerScale = 1;
                } else {
                    baseMaxColors = 64;
                    bayerScale = 0;
                }
            } else {
                // 长视频：根据质量调整
                if (quality != null && quality >= 80) {
                    baseMaxColors = 256;
                    bayerScale = 2;
                } else if (quality != null && quality >= 60) {
                    baseMaxColors = 128;
                    bayerScale = 1;
                } else {
                    baseMaxColors = 64;
                    bayerScale = 0;
                }
            }

            // 【色彩优化】根据分辨率调整颜色数，但保证最低质量
            if (totalPixels < 500000) {
                // 低分辨率（<50万像素）：适当减少颜色数
                maxColors = Math.max(128, (int) (baseMaxColors * 0.85));  // 提高最低颜色数
            } else if (totalPixels < 1000000) {
                // 中等分辨率（50-100万像素）：保持基础颜色数
                maxColors = baseMaxColors;
            } else if (totalPixels < 2000000) {
                // 高分辨率（100-200万像素）：保持256色（保证色彩）
                maxColors = Math.min(256, baseMaxColors);  // 不再强制减少到128
            } else {
                // 超高分辨率（>200万像素）：保持256色
                maxColors = Math.min(256, baseMaxColors);  // 不再强制减少到128
            }

            // 【平衡优化】使用full模式，速度和色彩的最佳平衡
            // full模式：比diff快，比single准确
            statsMode = "full";

            // 【平衡优化】保持合理的抖动
            // 对于超高分辨率，适当减少抖动以提速（色彩影响不大）
            if (totalPixels > 2000000 && bayerScale > 1) {
                bayerScale = bayerScale - 1;
                log.info("超高分辨率抖动优化 - bayer_scale从{}调整到{}以提速", bayerScale + 1, bayerScale);
            }

            log.info("调色板颜色数优化 - 分辨率: {}x{} ({}万像素), 基础颜色数: {}, 最终颜色数: {}, bayer_scale: {}",
                    outputWidth, outputHeight, String.format("%.1f", totalPixels / 10000.0),
                    baseMaxColors, maxColors, bayerScale);

            // 【平衡优化】调色板生成策略：速度优先，保证基本色彩
            if (isHighResolution) {
                // 高分辨率（>100万像素）：根据分辨率动态调整缩小比例
                int scaleDivisor;
                if (totalPixels > 2000000) {
                    // 超高分辨率（>200万像素）：缩小到1/3（速度提升9倍）
                    scaleDivisor = 3;
                } else {
                    // 高分辨率（100-200万像素）：缩小到1/2（速度提升4倍）
                    scaleDivisor = 2;
                }

                int paletteWidth = Math.max(320, outputWidth / scaleDivisor);
                int paletteHeight = Math.max(240, outputHeight / scaleDivisor);

                // 保持完整颜色数
                int paletteColors = maxColors;

                // 两阶段调色板生成：使用bilinear算法（速度快）
                filter.append(",split[s0][s1];")
                        .append("[s0]scale=").append(paletteWidth).append(":").append(paletteHeight)
                        .append(":flags=bilinear,")  // 使用bilinear算法（速度快）
                        .append("palettegen=max_colors=").append(paletteColors)
                        .append(":stats_mode=").append(statsMode)
                        .append("[p];")
                        .append("[s1][p]paletteuse=dither=bayer:bayer_scale=").append(bayerScale)
                        .append(":diff_mode=rectangle");

                log.info("高分辨率平衡优化 - 分辨率: {}x{} ({}万像素), 调色板生成尺寸: {}x{} (缩小到1/{}), 调色板颜色数: {}, stats_mode: {}, 预计提速: {}倍",
                        outputWidth, outputHeight, String.format("%.1f", totalPixels / 10000.0),
                        paletteWidth, paletteHeight, scaleDivisor, paletteColors, statsMode, scaleDivisor * scaleDivisor);
            } else {
                // 标准分辨率：使用原尺寸生成调色板
                filter.append(",split[s0][s1];[s0]palettegen=max_colors=").append(maxColors)
                        .append(":stats_mode=").append(statsMode)
                        .append("[p];")
                        .append("[s1][p]paletteuse=dither=bayer:bayer_scale=").append(bayerScale)
                        .append(":diff_mode=rectangle");

                log.info("标准分辨率平衡优化 - 分辨率: {}x{}, maxColors: {}, stats_mode: {}, bayer_scale: {}",
                        outputWidth, outputHeight, maxColors, statsMode, bayerScale);
            }

            command.add("-vf");
            command.add(filter.toString());

            // 设置输出帧率（控制GIF播放速度）
            command.add("-r");
            command.add(String.valueOf(targetFps));

            // 设置循环次数（0表示无限循环）
            command.add("-loop");
            command.add("0");

            // 【平衡优化】智能线程数：短视频也使用多线程
            // FFmpeg的多线程主要用于解码和编码，即使短视频也能受益
            int availableCores = Runtime.getRuntime().availableProcessors();
            int threads;

            if (videoDuration < 5) {
                // 极短视频（<5秒）：使用一半核心（避免过度开销）
                threads = Math.max(2, availableCores / 2);
            } else if (videoDuration < 30) {
                // 短视频（5-30秒）：使用所有核心（充分利用CPU）
                threads = availableCores;
            } else {
                // 长视频（>30秒）：使用所有核心
                threads = availableCores;
            }

            command.add("-threads");
            command.add(String.valueOf(threads));
            log.info("视频时长: {}秒, CPU核心数: {}, 使用线程数: {}", videoDuration, availableCores, threads);

            // 性能优化：使用ultrafast预设（最快速度）
            command.add("-preset");
            command.add("ultrafast");

            // 覆盖已存在的文件
            command.add("-y");

            command.add(outputPath);

            log.info("执行FFmpeg命令（已优化）: {}", String.join(" ", command));

            // 执行命令
            ProcessBuilder processBuilder = new ProcessBuilder(command);
            processBuilder.redirectErrorStream(true);
            Process process = processBuilder.start();

            // 读取输出和错误信息（使用独立线程避免阻塞）
            StringBuilder errorOutput = new StringBuilder();
            Thread outputThread = new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        log.debug("FFmpeg输出: {}", line);
                        // 收集错误信息
                        if (line.toLowerCase().contains("error") ||
                                line.toLowerCase().contains("failed") ||
                                line.toLowerCase().contains("invalid") ||
                                line.toLowerCase().contains("cannot")) {
                            errorOutput.append(line).append("\n");
                        }
                    }
                } catch (IOException e) {
                    log.warn("读取FFmpeg输出时出错", e);
                }
            });
            outputThread.start();

            int exitCode = process.waitFor();
            outputThread.join(2000); // 等待输出线程最多2秒

            if (exitCode != 0) {
                String errorMsg = "退出码: " + exitCode;
                if (errorOutput.length() > 0) {
                    errorMsg += "\n错误详情: " + errorOutput.toString();
                }
                log.error("FFmpeg转换失败: {}", errorMsg);
                throw new BusinessException(ErrorCode.FFMPEG_EXECUTION_FAILED, errorMsg);
            }

            // 检查输出文件是否存在
            java.io.File outputFile = new java.io.File(outputPath);
            if (!outputFile.exists()) {
                throw new BusinessException(ErrorCode.GIF_GENERATION_FAILED);
            }

            log.info("视频转GIF成功: {}, 文件大小: {} bytes, FPS: {}",
                    outputPath, outputFile.length(), fps);

        } catch (IOException | InterruptedException e) {
            log.error("视频转GIF失败", e);
            throw new BusinessException(ErrorCode.VIDEO_TO_GIF_FAILED, e.getMessage(), e);
        }
    }

    /**
     * 获取视频时长（秒）
     *
     * @param videoPath 视频文件路径
     * @return 时长（秒）
     */
    public static Double getVideoDuration(String videoPath) {
        try {
            List<String> command = new ArrayList<>();
            command.add(FFPROBE_PATH);
            command.add("-v");
            command.add("error");
            command.add("-show_entries");
            command.add("format=duration");
            command.add("-of");
            command.add("default=noprint_wrappers=1:nokey=1");
            command.add(videoPath);

            ProcessBuilder processBuilder = new ProcessBuilder(command);
            processBuilder.redirectErrorStream(true);
            Process process = processBuilder.start();

            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String duration = reader.readLine();

            int exitCode = process.waitFor();
            if (exitCode == 0 && duration != null) {
                return Double.parseDouble(duration);
            }

            return null;
        } catch (Exception e) {
            log.error("获取视频时长失败", e);
            return null;
        }
    }

    /**
     * 获取图片/GIF尺寸
     *
     * @param imagePath 图片文件路径
     * @return 尺寸数组 [宽度, 高度]
     */
    public static int[] getImageDimensions(String imagePath) {
        try {
            List<String> command = new ArrayList<>();
            command.add(FFPROBE_PATH);
            command.add("-v");
            command.add("error");
            command.add("-select_streams");
            command.add("v:0");
            command.add("-show_entries");
            command.add("stream=width,height");
            command.add("-of");
            command.add("csv=p=0");
            command.add(imagePath);

            ProcessBuilder processBuilder = new ProcessBuilder(command);
            processBuilder.redirectErrorStream(true);
            Process process = processBuilder.start();

            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String dimensions = reader.readLine();

            int exitCode = process.waitFor();
            if (exitCode == 0 && dimensions != null) {
                String[] parts = dimensions.split(",");
                if (parts.length == 2) {
                    return new int[]{Integer.parseInt(parts[0]), Integer.parseInt(parts[1])};
                }
            }

            return new int[]{0, 0};
        } catch (Exception e) {
            log.error("获取图片尺寸失败", e);
            return new int[]{0, 0};
        }
    }

    /**
     * 提取视频指定时间点的帧作为缩略图（已优化性能）
     *
     * @param videoPath 视频文件路径
     * @param outputPath 输出图片路径
     * @param width 输出宽度（可为null，自动计算）
     * @param height 输出高度（可为null，自动计算）
     * @param startTime 提取帧的时间点（秒），如果为null则提取第一帧
     */
    public static void extractFrameAtTime(String videoPath, String outputPath, Integer width, Integer height, Double startTime) {
        try {
            List<String> command = new ArrayList<>();
            command.add(FFMPEG_PATH);

            // 性能优化：先定位到时间点（放在-i之前更快）
            if (startTime != null && startTime > 0) {
                command.add("-ss");
                command.add(String.format("%.2f", startTime));
            }

            // 缩略图提取：不使用硬件加速（单帧提取，硬件加速开销大于收益）
            // 性能优化：禁用音频处理
            command.add("-an");

            command.add("-i");
            command.add(videoPath);

            // 只提取一帧
            command.add("-vframes");
            command.add("1");

            // 如果指定了尺寸，添加缩放滤镜（使用快速的bilinear算法）
            if (width != null || height != null) {
                StringBuilder filter = new StringBuilder("scale=");
                if (width != null && height != null) {
                    filter.append(width).append(":").append(height);
                } else if (width != null) {
                    filter.append(width).append(":-1");
                } else {
                    filter.append("-1:").append(height);
                }
                filter.append(":flags=bilinear"); // 使用bilinear算法（稳定且快速）

                command.add("-vf");
                command.add(filter.toString());
            }

            // 设置输出质量（缩略图使用中等质量即可）
            command.add("-q:v");
            command.add("5"); // 中等质量JPEG，比3稍快

            // 覆盖已存在的文件
            command.add("-y");

            command.add(outputPath);

            log.info("执行FFmpeg提取帧命令（已优化）: {}, 时间点: {}秒", String.join(" ", command), startTime);

            // 执行命令
            ProcessBuilder processBuilder = new ProcessBuilder(command);
            processBuilder.redirectErrorStream(true);
            Process process = processBuilder.start();

            // 读取输出和错误信息（使用独立线程避免阻塞）
            StringBuilder errorOutput = new StringBuilder();
            Thread outputThread = new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        log.debug("FFmpeg输出: {}", line);
                        // 收集错误信息
                        if (line.toLowerCase().contains("error") ||
                                line.toLowerCase().contains("failed") ||
                                line.toLowerCase().contains("invalid")) {
                            errorOutput.append(line).append("\n");
                        }
                    }
                } catch (IOException e) {
                    log.warn("读取FFmpeg输出时出错", e);
                }
            });
            outputThread.start();

            int exitCode = process.waitFor();
            outputThread.join(2000); // 等待输出线程最多2秒

            if (exitCode != 0) {
                String errorMsg = "提取帧失败，退出码: " + exitCode;
                if (errorOutput.length() > 0) {
                    errorMsg += "\n错误详情: " + errorOutput.toString();
                }
                log.error("FFmpeg提取帧失败: {}", errorMsg);
                throw new BusinessException(ErrorCode.FFMPEG_EXECUTION_FAILED, errorMsg);
            }

            // 检查输出文件是否存在
            java.io.File outputFile = new java.io.File(outputPath);
            if (!outputFile.exists()) {
                throw new BusinessException(ErrorCode.FFMPEG_EXECUTION_FAILED, "帧图片生成失败");
            }

            log.info("视频帧提取成功: {}, 时间点: {}秒, 文件大小: {} bytes", outputPath, startTime, outputFile.length());

        } catch (IOException | InterruptedException e) {
            log.error("提取视频帧失败", e);
            throw new BusinessException(ErrorCode.VIDEO_TO_GIF_FAILED, "提取帧失败: " + e.getMessage(), e);
        }
    }

    /**
     * 提取视频首帧（兼容旧方法）
     *
     * @param videoPath 视频文件路径
     * @param outputPath 输出图片路径
     * @param width 输出宽度（可为null，自动计算）
     * @param height 输出高度（可为null，自动计算）
     */
    public static void extractFirstFrame(String videoPath, String outputPath, Integer width, Integer height) {
        extractFrameAtTime(videoPath, outputPath, width, height, null);
    }

    /**
     * 检查FFmpeg是否可用
     *
     * @return true表示可用
     */
    public static boolean isFFmpegAvailable() {
        try {
            Process process = Runtime.getRuntime().exec(new String[]{FFMPEG_PATH, "-version"});
            int exitCode = process.waitFor();
            return exitCode == 0;
        } catch (Exception e) {
            return false;
        }
    }
}


